package humynn.sbd
{
	import flash.display.BitmapData;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.media.SoundChannel;
	import flash.utils.Timer;
	
	import humynn.engine.Entity;
	import humynn.engine.ImageManager;
	import humynn.engine.Input;
	import humynn.engine.Renderer;
	import humynn.engine.SoundManager;
	
	/**
	 * The stink bug class. The game's antagonist.
	 */
	public class StinkBug extends Entity
	{
		private const FLYING:uint = 0;
		private const CRAWLING:uint = 1;
		private const STOPPED:uint = 2;
		
		private const MAX_FLY_SPEED:Number = 5;
		private const MAX_CRAWL_SPEED:Number = 2;
		
		private var _speed:Point;
		private var _state:uint;
		
		private var _animTimer:Timer;
		private var _landTimer:Timer;
		private var _crawlTimer:Timer;
		
		private var _facingRight:Boolean;
		private var _rotation:Number;
		
		private var _oldlmb:Boolean;
		
		private var _sndStop:SoundChannel;
		
		public function StinkBug()
		{
			super();
			
			_speed = new Point();
			
			_state = FLYING;
			
			_facingRight = true;
			_rotation = 0;
			
			_oldlmb = false;
			
			_animTimer = new Timer(20);
			_animTimer.addEventListener(TimerEvent.TIMER, updateAnimation);
			
			_landTimer = new Timer(3000, 1);
			_landTimer.addEventListener(TimerEvent.TIMER_COMPLETE, crawl);
			
			_crawlTimer = new Timer(3000, 1);
			_crawlTimer.addEventListener(TimerEvent.TIMER_COMPLETE, stopMoving);
			
			_animTimer.start();
			_landTimer.start();
		}
		
		/**
		 * Checks if the stink bug is damaging health.
		 * 
		 * @returns If the stink bug is damaging health.
		 */
		public function get spraying():Boolean
		{
			return _state == STOPPED;
		}
		
		/**
		 * Gets the rotation of the stink bug.
		 * 
		 * @returns The rotation angle.
		 */
		public function get rotation():Number
		{
			return _rotation;
		}
		
		/**
		 * Sets the rotation of the stink bug.
		 * 
		 * @param value The rotation angle.
		 */
		public function set rotation(value:Number):void
		{
			_rotation = value;
		}
		
		public function set stateDelay(value:Number):void
		{
			_landTimer.delay = value * 1000;
			_crawlTimer.delay = value * 1000;
		}
		
		/**
		 * Stops the stink bug.
		 */
		public function stop():void
		{
			_animTimer.stop();
			_landTimer.stop();
			_crawlTimer.stop();
			
			if(_sndStop != null)
			{
				_sndStop.stop();
			}
		}
		
		/**
		 * Called when the stink bug is done flying. Causes the
		 * stink bug to enter the crawling state.
		 * 
		 * @param e Ignored.
		 */
		private function crawl(e:TimerEvent):void
		{
			SoundManager.play("ahh.mp3");
			
			_state = CRAWLING;
			
			_animTimer.delay = 250;
			
			_animIndex = CRAWLING;
			_animFrame = 0;
			
			_speed.x = _speed.x / 4.0;
			_speed.y = _speed.y / 4.0;
			
			_crawlTimer.start();
		}
		
		/**
		 * Called when the stink bug is done crawling. Causes the
		 * stink bug to enter the health damaging state.
		 * 
		 * @param e Ignored.
		 */
		private function stopMoving(e:TimerEvent):void
		{
			_sndStop = SoundManager.play("releasing.mp3", true);
			
			_state = STOPPED;
			
			_animTimer.delay = 250;
			
			_animIndex = STOPPED;
			_animFrame = 0;
		}
		
		/**
		 * Adjusts the frame of the animation.
		 * 
		 * @param e Ignored.
		 */
		private function updateAnimation(e:TimerEvent):void
		{
			var maxFrames:uint = ImageManager.getFrameCount(_imageFile, _state);
			
			_animFrame++;
			if(_animFrame >= maxFrames)
			{
				_animFrame = 0;
			}
		}
		
		/**
		 * Updates the stink bug based on a randomly generated angle of
		 * rotation for the direction. It moves the stink bug based on a
		 * speed vector. Also checks to see if it has been disturbed (clicked).
		 * 
		 * @param input The user input.
		 */
		public override function update(input:Input):void
		{
			var rotationDelta:Number = 0;
			var rect:Rectangle = new Rectangle(x, y, 96, 96);
			
			if(rect.contains(input.mouse.x, input.mouse.y))
			{
				if(input.mouse.lmbutton && !_oldlmb)
				{
					reset();
				}
			}
			
			_oldlmb = input.mouse.lmbutton;
			
			if(_state == FLYING)
			{
				rotationDelta = (Math.random() * 0.4) - 0.2;
				
				_rotation += rotationDelta;
				
				_speed.x = Math.cos(_rotation);
				_speed.y = Math.sin(_rotation);
				
				_speed.normalize(MAX_FLY_SPEED);
				
				if(_speed.x > 0)
				{
					_facingRight = true;
				} else {
					_facingRight = false;
				}
			}
			else if(_state == CRAWLING)
			{
				_speed.x = Math.cos(_rotation);
				_speed.y = Math.sin(_rotation);
				
				_speed.normalize(MAX_CRAWL_SPEED);
			}
			else if(_state == STOPPED)
			{
				_speed.x = 0;
				_speed.y = 0;
			}
			
			x += _speed.x;
			y += _speed.y;
			
			if(x < 0)
			{
				x = 0;
				
				_rotation = Math.PI - _rotation;
			}
			else if(x > 800 - rect.width)
			{
				x = 800 - rect.width;
				
				_rotation = Math.PI - _rotation;
			}
			
			if(y < 0)
			{
				y = 0;
				
				_rotation = -_rotation;
			}
			else if(y > 600 - rect.height)
			{
				y = 600 - rect.height;
				
				_rotation = -_rotation;
			}
		}
		
		/**
		 * Draws the stink bug in its current state.
		 * 
		 * @param renderer The renderer to draw to.
		 */
		public override function draw(renderer:Renderer):void
		{
			if(visible)
			{
				if(_state == FLYING)
				{
					renderer.draw(_imageFile, _animIndex, _animFrame, x, y, null, 0, _facingRight);
				}
				else
				{
					renderer.draw(_imageFile, _animIndex, _animFrame, x, y, null, _rotation + Math.PI/2);
				}
			}
		}
		
		/**
		 * Resets the stink bug. Called when the stink bug is disturbed
		 * by the user.
		 */
		private function reset():void
		{
			if(_sndStop != null)
			{
				_sndStop.stop();
			}
			SoundManager.play("squeak.mp3");
			
			_state = FLYING;
			
			_animIndex = FLYING;
			
			_animTimer.delay = 20;
			
			_landTimer.reset();
			_crawlTimer.reset();
			
			_landTimer.start();
		}
	}
}